#include "stdafx.h"
#include "RenderHeightMap.h"
//#define  NDF_DIM  512
#define  SCALEFACTOR 100

CRenderHeightMap::CRenderHeightMap(void)
{
	m_pXYZFile = NULL;
	m_bDataReady = false;

	m_pWorldViewProjection = NULL;
	m_pRenderHeightMapTech = NULL;
	m_pRenderHeightMapNormalTech = NULL;
	m_pVertexLayout = NULL;
	m_pEffect = NULL;
	m_pVB = NULL;
	m_pNormalLineVB = NULL;
	m_iNumOfNormLine = 0;
	m_pIB = NULL;

	m_pColorBar = NULL;
	m_vNormalLineColor = D3DXVECTOR4(1,1,1,1);
	m_fNormalLineScale = 0.24f;
	m_bRenderNormal = true;
	m_bRenderWireFrame = false;
	D3DXMatrixIdentity(&m_matScaling);
}

CRenderHeightMap::~CRenderHeightMap(void)
{
	OnD3D11DestroyDevice(NULL);
	SAFE_DELETE(m_pColorBar);
}

void CRenderHeightMap::SetXYZFilePointer(CXYZFileIO* pXYZFile)
{
	if(pXYZFile==NULL && !(pXYZFile->m_bDataReady))
	{
		printf("Error!\n");
		return;
	}
	m_pXYZFile=pXYZFile;	
}

HRESULT CRenderHeightMap::SetNormalWndSize(ID3D11Device* pd3dDevice, int iSize)
{
	HRESULT hr = S_OK;

	bool bRt = m_pXYZFile->SetNewNormalWndSize(iSize);
	if(!bRt) 
		return E_FAIL;
	V(prepareNormalLineVB(pd3dDevice));
	
	m_bDataReady = true;
	CString strTmp;
	strTmp.Format(_T("New Normal Window Size:%d"),iSize);
	theApp.OutputString(strTmp);

	return hr;
}

HRESULT CRenderHeightMap::SetNFGaussianSigma(ID3D11Device* pd3dDevice, float fNewSigma)
{
	HRESULT hr = S_OK;

	bool bRt = m_pXYZFile->SetGaussianSigma(fNewSigma);
	if(!bRt) 
		return E_FAIL;
	V(prepareNormalLineVB(pd3dDevice));

	m_bDataReady = true;
	CString strTmp;
	strTmp.Format(_T("New Normal Filtering Gaussian Sigma:%f"),fNewSigma);
	theApp.OutputString(strTmp);

	return hr;
}

HRESULT CRenderHeightMap::PreparedXYZRenderBufs(ID3D11Device* pd3dDevice)
{
	if(m_pXYZFile == NULL||!(m_pXYZFile->m_bDataReady))
		return E_FAIL;
	//Recreate VB and IB
	//create vertex buffer
	HRESULT hr = CD3DUtilityFunction::CreateVBVec3(pd3dDevice, m_pXYZFile->m_iHMDataWidth, m_pXYZFile->m_iHMDataHeight, m_pXYZFile->m_fSampleWidth, 
		m_pXYZFile->m_fSampleHeight, -0.5f, -0.5f, m_pXYZFile->m_arrHeightMapData, &m_pVB);
	//create index buffer
	hr = CD3DUtilityFunction::CreateIBGrid32Bit(pd3dDevice, m_pXYZFile->m_iHMDataWidth, m_pXYZFile->m_iHMDataHeight, &m_pIB);

	//create normal buffer
	//m_pXYZFile->EstimateNormalForEachPoint();
	hr = prepareNormalLineVB(pd3dDevice);		

	m_bDataReady = true;
	//prepare scaling matrix
	float iDim = static_cast<float>(m_pXYZFile->m_iHMDataWidth > m_pXYZFile->m_iHMDataHeight ? m_pXYZFile->m_iHMDataWidth:m_pXYZFile->m_iHMDataHeight);
	D3DXMatrixScaling(&m_matScaling,SCALEFACTOR/iDim,SCALEFACTOR/iDim,SCALEFACTOR/iDim);

	return hr;
}

void CRenderHeightMap::InitEffectsVariables()
{
	m_pRenderHeightMapTech = m_pEffect->GetTechniqueByName( "RenderHeightMap" );
	m_pRenderHeightMapWFTech = m_pEffect->GetTechniqueByName("RenderHeightMap_WIRE");
	m_pRenderHeightMapNormalTech = m_pEffect->GetTechniqueByName("RenderHeightMapNormal");
	m_pWorldViewProjection = m_pEffect->GetVariableByName( "mWorldViewProjection" )->AsMatrix();
	m_pHeightRange = m_pEffect->GetVariableByName("mHeightRange")->AsScalar();
	m_pNormalDebugColor = m_pEffect->GetVariableByName("vNormalDebugColor")->AsVector();
	m_pColorBarTex = m_pEffect->GetVariableByName("texColorBar")->AsShaderResource();
}

HRESULT CRenderHeightMap::OnD3D11CreateDevice(ID3D11Device* pd3dDevice, CDirectoryUtil* pDirUtil, void* pUserContext)
{
	if(m_pXYZFile==NULL)	
		printf("No .xyz file loaded!\n");

	HRESULT hr = S_OK;
	
	V(PreparedXYZRenderBufs(pd3dDevice));

	//create effects
	TCHAR ShaderFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderHeightMap.fx",subfolder, ShaderFileDir);
	hr = CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect);	
	InitEffectsVariables();
	//create vertex layout
	const D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };
	hr = CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,layout, ARRAYSIZE(layout), m_pRenderHeightMapTech,&m_pVertexLayout);   
	DXUT_SetDebugName( m_pVertexLayout, "VBLayout" );
	
	if(hr != S_OK)
		m_bDataReady = false;

	//assist
	SAFE_DELETE(m_pColorBar);
	m_pColorBar = new CRenderColorBar(256);
	hr = m_pColorBar->OnD3D11CreateDevice(pd3dDevice,pDirUtil,pUserContext);

	return hr;
}

void CRenderHeightMap::ReloadEffectsDebug(ID3D11Device* pd3dDevice, CDirectoryUtil* pDirUtil)
{
	HRESULT hr = S_OK;
	SAFE_RELEASE(m_pEffect);
	//create effects
	TCHAR ShaderFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderHeightMap.fx",subfolder, ShaderFileDir);
	hr = CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect);	
	InitEffectsVariables();

	m_pColorBar->ReloadEffectsDebug(pd3dDevice,pDirUtil);
}

HRESULT CRenderHeightMap::OnD3D11ResizedSwapChain(ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                         const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, CDirectoryUtil* pDirUtil, void* pUserContext)
{
	HRESULT hr = S_OK;
	hr = m_pColorBar->OnD3D11ResizedSwapChain(pd3dDevice,pSwapChain,pBackBufferSurfaceDesc,pDirUtil,pUserContext);
	return hr;
}

void CRenderHeightMap::OnD3D11ReleasingSwapChain(void* pUserContext)
{
	m_pColorBar->OnD3D11ReleasingSwapChain(pUserContext);
}
void CRenderHeightMap::OnD3D11DestroyDevice( void* pUserContext )
{
	SAFE_RELEASE(m_pEffect);
	SAFE_RELEASE(m_pVB);
	SAFE_RELEASE(m_pNormalLineVB);
	SAFE_RELEASE(m_pIB);
	SAFE_RELEASE(m_pVertexLayout);

	m_pColorBar->OnD3D11DestroyDevice(pUserContext);

}

void CRenderHeightMap::OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, CBaseCamera* pViewCamera, 
	CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, void* pUserContext )
{
	HRESULT hr = S_OK;
	if(!m_bDataReady)
	{
		printf("Data is NOT ready!\n");
		return;
	}

    // Get the projection & view matrix from the camera class
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
    D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
	mWorld = mWorld*m_matScaling;
	D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
    D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
    D3DXMATRIX mWorldViewProjection = mWorld * mView * mProj;
	
	V(m_pWorldViewProjection->SetMatrix((float*)(&mWorldViewProjection)));
	V(m_pHeightRange->SetFloat(m_pXYZFile->m_fHeightRange));
	V(m_pColorBarTex->SetResource(m_pColorBar->m_pColorBarTexSR));

	// Set render resources
    pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = { m_pVB };
    UINT pStride[1] = { sizeof(D3DXVECTOR3) };
    UINT pOffset[1] = { 0 };
    pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetIndexBuffer(m_pIB, DXGI_FORMAT_R32_UINT, 0);
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	ID3DX11EffectTechnique* pTech = m_pRenderHeightMapTech;
	if(m_bRenderWireFrame)
		pTech = m_pRenderHeightMapWFTech;
	pTech->GetDesc( &Desc );

	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		pTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->DrawIndexed( (m_pXYZFile->m_iHMDataWidth-1)*(m_pXYZFile->m_iHMDataHeight-1)*6, 0, 0 );				
	}

	//render normal
	if(m_bRenderNormal && m_iNumOfNormLine > 0)
		RenderNormalLines(pd3dDevice,pd3dImmediateContext,pViewCamera,pDirUtil,fTime,fElapsedTime,m_pXYZFile->GetCurNormalEstiMethod(),pUserContext);	
	
	//render color bar
	//m_pColorBar->OnD3D11FrameRender(pd3dDevice,pd3dImmediateContext,pViewCamera,pDirUtil,fTime,fElapsedTime,pUserContext);
}

HRESULT CRenderHeightMap::SetNormalLineScale(ID3D11Device* pd3dDevice, float fScale)
{
	if(fScale == m_fNormalLineScale)
		return E_FAIL;
	float fOldLineScale = m_fNormalLineScale;
	m_fNormalLineScale = fScale;

	HRESULT hr = S_OK;
	if(FAILED(prepareNormalLineVB(pd3dDevice)))
	{
		m_fNormalLineScale = fOldLineScale;
		return E_FAIL;
	}

	return hr;
}

#if 0
HRESULT CRenderHeightMap::prepareNormalLineVB(ID3D11Device* pd3dDevice)
{
	size_t iNumElem = m_pXYZFile->m_arrNormalAreaWeighted.size();
	if(iNumElem == 0)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
		return E_FAIL;
	}

	CGrowableArray<D3DXVECTOR3> arrVBData;
	int i2DNDFImgDim = m_pXYZFile->m_i2DNDFImgDim;
	
	m_iNumOfNormLine = 0;
	for(size_t i = 0; i < iNumElem; i++)
	{
		D3DXVECTOR3 norm = m_pXYZFile->m_arrNormalAreaWeighted[i].Normal;
		norm.x = norm.x/2.0f + 0.5f; //(-1,1) --> (0,1); 
		norm.y = norm.y/2.0f + 0.5f;
		D3DXVECTOR3 mNorm(0,0,1);
		float ldotn = D3DXVec3Dot(&norm,&mNorm);
		float theta = acos(ldotn)*RADIAN2DEGREE;

#if 0
		if(theta >= 90)
		{
			CString strDebug;
			strDebug.Format(L"NegNorm Error: ldotn=%f  theta=%f", ldotn, theta);
			theApp.OutputString(strDebug);
		}
#endif
		//int iPosX = (int)(floorf(norm.x * i2DNDFImgDim));
		//int iPosY = (int)(floorf(norm.y * i2DNDFImgDim));
		//int iYDiff = abs(iPosY - i2DNDFImgDim/2);
		//if( iYDiff<=10 && (iPosX<=100 || iPosX>=(i2DNDFImgDim-100) ) ) 
		//if( iYDiff<=10 && (iPosX<=10 || iPosX>=(i2DNDFImgDim-10) ) ) 
		if ( (90-theta) < 5 )
		{
			D3DXVECTOR3 pos1 = m_pXYZFile->m_arrNormalAreaWeighted[i].Position;
			D3DXVECTOR3 pos2 = pos1 + m_pXYZFile->m_arrNormalAreaWeighted[i].Normal*m_fNormalLineScale;

			arrVBData.Add(pos1);
			arrVBData.Add(pos2);

			m_iNumOfNormLine++;
		}		
	}

	CString strDebug;
	strDebug.Format(L"number of normal lines: %d", m_iNumOfNormLine);
	theApp.OutputString(strDebug);

	HRESULT hr = S_OK;
	//release old buffer
	SAFE_RELEASE(m_pNormalLineVB);
	if(m_iNumOfNormLine > 0)
		V(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,sizeof(D3DXVECTOR3),(float*)(arrVBData.GetData()),(m_iNumOfNormLine*2),&m_pNormalLineVB));

	arrVBData.RemoveAll();	
	return hr;
}
#else
HRESULT CRenderHeightMap::prepareNormalLineVB(ID3D11Device* pd3dDevice)
{
	size_t iNumElem = m_pXYZFile->m_arrNormalAreaWeighted.size();
	if(iNumElem == 0)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
		return E_FAIL;
	}

	D3DXVECTOR3* pVBData = new D3DXVECTOR3[iNumElem*2];
	m_iNumOfNormLine = 0;
	for(size_t i = 0; i < iNumElem; i++)
	{
		*(pVBData+2*i) = m_pXYZFile->m_arrNormalAreaWeighted[i].Position;
		*(pVBData+2*i+1) = m_pXYZFile->m_arrNormalAreaWeighted[i].Normal*m_fNormalLineScale + *(pVBData+2*i);
		//printf("Normal line: (%f,%f,%f) to (%f,%f,%f)\n",(pVBData+2*i)->x, (pVBData+2*i)->y, (pVBData+2*i)->z, (pVBData+2*i+1)->x, (pVBData+2*i+1)->y, (pVBData+2*i+1)->z);
		m_iNumOfNormLine++;
	}

	CString strDebug;
	strDebug.Format(L"number of normal lines: %d", m_iNumOfNormLine);
	theApp.OutputString(strDebug);

	HRESULT hr = S_OK;
	//release old buffer
	SAFE_RELEASE(m_pNormalLineVB);
	V(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,sizeof(D3DXVECTOR3),(float*)(pVBData),(m_iNumOfNormLine*2),&m_pNormalLineVB));

	SAFE_DELETE_ARRAY(pVBData);	
	return hr;
}
#endif

//HRESULT CRenderHeightMap::prepareNormalLineVB1(ID3D11Device* pd3dDevice)
//{
//	unsigned int iNumElem = m_pXYZFile->m_arrNormalAreaWeighted.size();
//	if(iNumElem == 0)
//	{
//#ifdef _DEBUG
//		OUTPUTERRORINFO;
//#endif
//			return E_FAIL;
//	}
//
//	D3DXVECTOR3* pVBData = new D3DXVECTOR3[iNumElem*2];
//	for(int i = 0; i < iNumElem; i++)
//	{
//		*(pVBData+2*i) = m_pXYZFile->m_arrNormalAreaWeighted[i].Position;
//		*(pVBData+2*i+1) = m_pXYZFile->m_arrNormalAreaWeighted[i].Normal*m_fNormalLineScale + *(pVBData+2*i);		
//	}
//	
//	HRESULT hr = S_OK;
//	//release old buffer
//	SAFE_RELEASE(m_pNormalLineVB1);
//	V(CD3DUtilityFunction::CreateVertexBuffer(pd3dDevice,sizeof(D3DXVECTOR3),(float*)(pVBData),iNumElem*2,&m_pNormalLineVB1));
//
//	SAFE_DELETE_ARRAY(pVBData);	
//	return hr;
//}

void CRenderHeightMap::RenderNormalLines(ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, 
	CBaseCamera* pViewCamera, CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, NORMALESTIMATEMETHOD iCurNormalMethod, void* pUserContext)
{
	HRESULT hr = S_OK;
	if(!m_bDataReady)
	{
		printf("Data is NOT ready!\n");
		return;
	}

	// Get the projection & view matrix from the camera class
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
	D3DXMATRIX mWorld = *(pCurCamera->GetWorldMatrix());
	mWorld = mWorld*m_matScaling;
	D3DXMATRIX mView = *(pCurCamera->GetViewMatrix());
	D3DXMATRIX mProj = *(pCurCamera->GetProjMatrix());
	D3DXMATRIX mWorldViewProjection = mWorld * mView * mProj;
	hr = m_pWorldViewProjection->SetMatrix((float*)(&mWorldViewProjection));

	m_pNormalDebugColor->SetFloatVector((float*)&m_vNormalLineColor);
	// Set render resources
	pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = {m_pNormalLineVB};
	UINT pStride[1] = { sizeof(D3DXVECTOR3) };
	UINT pOffset[1] = { 0 };
	pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINELIST);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	m_pRenderHeightMapNormalTech->GetDesc( &Desc );

	unsigned int iNumVertexDraw = static_cast<unsigned int>(m_iNumOfNormLine*2);
	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		m_pRenderHeightMapNormalTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->Draw(iNumVertexDraw,0);		
	}
}

